{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 数据处理"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 安装类库\n",
    "# !mkdir /home/aistudio/external-libraries\n",
    "# !pip install imgaug -t /home/aistudio/external-libraries\n",
    "import sys\n",
    "sys.path.append('/home/aistudio/external-libraries')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "image shape: (32, 32, 3)\n",
      "label value: cattle\n"
     ]
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 216x216 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "import paddle\n",
    "import numpy as np\n",
    "from PIL import Image\n",
    "import matplotlib.pyplot as plt\n",
    "import imgaug as ia\n",
    "import imgaug.augmenters as iaa\n",
    "\n",
    "# 读取数据\n",
    "reader = paddle.batch(\n",
    "    paddle.dataset.cifar.train100(),\n",
    "    batch_size=8) # 数据集读取器\n",
    "data = next(reader()) # 读取数据\n",
    "index = 0 # 批次索引\n",
    "\n",
    "# 读取图像\n",
    "image = np.array([x[0] for x in data]).astype(np.float32) # 读取图像数据，数据类型为float32\n",
    "image = image * 255 # 从[0,1]转换到[0,255]\n",
    "image = image[index].reshape((3, 32, 32)).transpose((1, 2, 0)).astype(np.uint8) # 数据格式从CHW转换为HWC，数据类型转换为uint8\n",
    "print('image shape:', image.shape)\n",
    "\n",
    "# 图像增强\n",
    "# sometimes = lambda aug: iaa.Sometimes(0.5, aug) # 随机进行图像增强\n",
    "# seq = iaa.Sequential([\n",
    "#     sometimes(iaa.CropAndPad(px=(-4, 4))),      # 随机裁剪填充像素\n",
    "#     iaa.Fliplr(0.5)])                           # 随机进行水平翻转\n",
    "# image = seq(image=image)\n",
    "\n",
    "# 读取标签\n",
    "label = np.array([x[1] for x in data]).astype(np.int64) # 读取标签数据，数据类型为int64\n",
    "vlist = ['beaver', 'dolphin', 'otter', 'seal', 'whale',\n",
    "         'aquarium fish', 'flatfish', 'ray', 'shark', 'trout',\n",
    "         'orchids', 'poppies', 'roses', 'sunflowers', 'tulips',\n",
    "         'bottles', 'bowls', 'cans', 'cups', 'plates',\n",
    "         'apples', 'mushrooms', 'oranges', 'pears', 'sweet peppers',\n",
    "         'clock', 'keyboard', 'lamp', 'telephone', 'television',\n",
    "         'bed', 'chair', 'couch', 'table', 'wardrobe',\n",
    "         'bee', 'beetle', 'butterfly', 'caterpillar', 'cockroach',\n",
    "         'bear', 'leopard', 'lion', 'tiger', 'wolf',\n",
    "         'bridge', 'castle', 'house', 'road', 'skyscraper',\n",
    "         'cloud', 'forest', 'mountain', 'plain', 'sea',\n",
    "         'camel', 'cattle', 'chimpanzee', 'elephant', 'kangaroo',\n",
    "         'fox', 'porcupine', 'possum', 'raccoon', 'skunk',\n",
    "         'crab', 'lobster', 'snail', 'spider', 'worm',\n",
    "         'baby', 'boy', 'girl', 'man', 'woman',\n",
    "         'crocodile', 'dinosaur', 'lizard', 'snake', 'turtle',\n",
    "         'hamster', 'mouse', 'rabbit', 'shrew', 'squirrel',\n",
    "         'maple', 'oak', 'palm', 'pine', 'willow',\n",
    "         'bicycle', 'bus', 'motorcycle', 'pickup truck', 'train',\n",
    "         'lawn-mower', 'rocket', 'streetcar', 'tank', 'tractor'] # 标签名称列表\n",
    "vlist.sort() # 字母上升排序\n",
    "print('label value:', vlist[label[index]])\n",
    "\n",
    "# 显示图像\n",
    "image = Image.fromarray(image)   # 转换图像格式\n",
    "image.save('./work/out/img.png') # 保存读取图像\n",
    "plt.figure(figsize=(3, 3))       # 设置显示大小\n",
    "plt.imshow(image)                # 设置显示图像\n",
    "plt.show()                       # 显示图像文件"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "train_data: image shape (128, 3, 32, 32), label shape:(128, 1)\n",
      "valid_data: image shape (128, 3, 32, 32), label shape:(128, 1)\n"
     ]
    }
   ],
   "source": [
    "import paddle\n",
    "import numpy as np\n",
    "import imgaug as ia\n",
    "import imgaug.augmenters as iaa\n",
    "\n",
    "# 训练数据增强\n",
    "def train_augment(images):\n",
    "    # 转换格式\n",
    "    images = images * 255 # 从[0,1]转换到[0,255]\n",
    "    images = images.transpose((0, 2, 3, 1)).astype(np.uint8) # 数据格式从BCHW转换为BHWC，数据类型转换为uint8\n",
    "    \n",
    "    # 增强图像\n",
    "    sometimes = lambda aug: iaa.Sometimes(0.5, aug) # 随机进行图像增强\n",
    "    seq = iaa.Sequential([\n",
    "        sometimes(iaa.CropAndPad(px=(-4, 4))),      # 随机裁剪填充像素\n",
    "        iaa.Fliplr(0.5)])                           # 随机进行水平翻转\n",
    "    images = seq(images=images)\n",
    "    \n",
    "    # 减去均值\n",
    "    mean = np.array([0.4914, 0.4822, 0.4465]).reshape((1, 1, 1, -1)) # cifar数据集通道平均值\n",
    "    stdv = np.array([0.2471, 0.2435, 0.2616]).reshape((1, 1, 1, -1)) # cifar数据集通道标准差\n",
    "    \n",
    "    images = (images/255.0 - mean) / stdv # 对图像进行归一化\n",
    "    images = images.transpose((0, 3, 1, 2)).astype(np.float32) # 数据格式从BHWC转换为BCHW，数据类型转换为float32\n",
    "    \n",
    "    return images\n",
    "\n",
    "# 验证数据增强\n",
    "def valid_augment(images):\n",
    "    # 转换格式\n",
    "    images = images * 255 # 从[0,1]转换到[0,255]\n",
    "    images = images.transpose((0, 2, 3, 1)).astype(np.uint8) # 数据格式从BCHW转换为BHWC，数据类型转换为uint8\n",
    "    \n",
    "    # 减去均值\n",
    "    mean = np.array([0.4914, 0.4822, 0.4465]).reshape((1, 1, 1, -1)) # cifar数据集通道平均值\n",
    "    stdv = np.array([0.2471, 0.2435, 0.2616]).reshape((1, 1, 1, -1)) # cifar数据集通道标准差\n",
    "    \n",
    "    images = (images/255.0 - mean) / stdv # 对图像进行归一化\n",
    "    images = images.transpose((0, 3, 1, 2)).astype(np.float32) # 数据格式从BHWC转换为BCHW，数据类型转换为float32\n",
    "    \n",
    "    return images\n",
    "\n",
    "# 读取训练数据\n",
    "train_reader = paddle.batch(\n",
    "    paddle.reader.shuffle(paddle.dataset.cifar.train100(), buf_size=50000),\n",
    "    batch_size=128) # 构造数据读取器\n",
    "train_data = next(train_reader()) # 读取训练数据\n",
    "\n",
    "train_image = np.array([x[0] for x in train_data]).reshape((-1, 3, 32, 32)).astype(np.float32) # 读取训练图像\n",
    "train_image = train_augment(train_image)                                                       # 训练图像增强\n",
    "train_label = np.array([x[1] for x in train_data]).reshape((-1, 1)).astype(np.int64)           # 读取训练标签\n",
    "print('train_data: image shape {}, label shape:{}'.format(train_image.shape, train_label.shape))\n",
    "\n",
    "# 读取验证数据\n",
    "valid_reader = paddle.batch(\n",
    "    paddle.dataset.cifar.test100(),\n",
    "    batch_size=128) # 构造数据读取器\n",
    "valid_data = next(valid_reader()) # 读取验证数据\n",
    "\n",
    "valid_image = np.array([x[0] for x in valid_data]).reshape((-1, 3, 32, 32)).astype(np.float32) # 读取验证图像\n",
    "valid_image = valid_augment(valid_image)                                                       # 验证图像增强\n",
    "valid_label = np.array([x[1] for x in valid_data]).reshape((-1, 1)).astype(np.int64)           # 读取验证标签\n",
    "print('valid_data: image shape {}, label shape:{}'.format(valid_image.shape, valid_label.shape))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 模型设计"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "import paddle.fluid as fluid\n",
    "from paddle.fluid.dygraph.nn import Conv2D, Pool2D, Linear, BatchNorm\n",
    "import math\n",
    "\n",
    "# 模组结构：输入维度，输出维度，滑动步长，基础长度\n",
    "group_arch = [(16, 16, 1, 18), (16, 32, 2, 18), (32, 64, 2, 18)]\n",
    "group_dim  = 64  # 模组输出维度\n",
    "class_dim  = 100 # 类别数量维度\n",
    "\n",
    "# 卷积单元\n",
    "class ConvUnit(fluid.dygraph.Layer):\n",
    "    def __init__(self, in_dim, out_dim, filter_size=3, stride=1, act=None):\n",
    "        \"\"\"\n",
    "        功能:\n",
    "            初始化卷积单元，H/W=(H/W+2*P-F)/S+1\n",
    "        输入:\n",
    "            in_dim      - 输入维度\n",
    "            out_dim     - 输出维度\n",
    "            filter_size - 卷积大小\n",
    "            stride      - 滑动步长\n",
    "            act         - 激活函数\n",
    "        输出:\n",
    "        \"\"\"\n",
    "        super(ConvUnit, self).__init__()\n",
    "        \n",
    "        # 添加卷积\n",
    "        self.conv = Conv2D(\n",
    "            num_channels=in_dim,\n",
    "            num_filters=out_dim,\n",
    "            filter_size=filter_size,\n",
    "            stride=stride,\n",
    "            padding=(filter_size-1)//2,                       # 输出特征图大小不变\n",
    "            param_attr=fluid.initializer.MSRA(uniform=False), # 使用MARA 初始权重\n",
    "            bias_attr=False,                                  # 卷积输出没有偏置项\n",
    "            act=None)\n",
    "        \n",
    "        # 添加正则\n",
    "        self.norm = BatchNorm(\n",
    "            num_channels=out_dim,\n",
    "            param_attr=fluid.initializer.Constant(1.0), # 使用常量初始化权重\n",
    "            bias_attr=fluid.initializer.Constant(0.0),  # 使用常量初始化偏置\n",
    "            act=act)\n",
    "    \n",
    "    def forward(self, x):\n",
    "        \"\"\"\n",
    "        功能:\n",
    "            对输入的特征进行卷积和正则\n",
    "        输入:\n",
    "            x - 输入特征\n",
    "        输出:\n",
    "            x - 输出特征\n",
    "        \"\"\"\n",
    "        # 进行卷积\n",
    "        x = self.conv(x)\n",
    "        \n",
    "        # 进行正则\n",
    "        x = self.norm(x)\n",
    "        \n",
    "        return x\n",
    "\n",
    "\n",
    "# 基础结构\n",
    "class ResBasic(fluid.dygraph.Layer):\n",
    "    def __init__(self, in_dim, out_dim, stride=1, is_pass=True):\n",
    "        \"\"\"\n",
    "        功能:\n",
    "            初始化基础结构，H/W=(H/W+2*P-F)/S+1\n",
    "        输入:\n",
    "            in_dim  - 输入维度\n",
    "            out_dim - 输出维度\n",
    "            stride  - 滑动步长\n",
    "            is_pass - 是否直连\n",
    "        输出:\n",
    "        \"\"\"\n",
    "        super(ResBasic, self).__init__()\n",
    "        \n",
    "        # 是否直连标识\n",
    "        self.is_pass = is_pass\n",
    "        \n",
    "        # 添加投影路径\n",
    "        self.proj = ConvUnit(in_dim=in_dim, out_dim=out_dim, filter_size=1, stride=stride, act=None)\n",
    "        \n",
    "        # 添加卷积路径\n",
    "        self.con1 = ConvUnit(in_dim=in_dim, out_dim=out_dim, filter_size=3, stride=stride, act='relu')\n",
    "        self.con2 = ConvUnit(in_dim=out_dim, out_dim=out_dim, filter_size=3, stride=1, act=None)\n",
    "        \n",
    "    def forward(self, x):\n",
    "        \"\"\"\n",
    "        功能:\n",
    "            对输入的特征图像提取特征\n",
    "        输入:\n",
    "            x - 输入特征\n",
    "        输出:\n",
    "            x - 输出特征\n",
    "            y - 输出特征\n",
    "        \"\"\"\n",
    "        # 直连路径\n",
    "        if self.is_pass: # 是否直连\n",
    "            x_pass = x\n",
    "        else:            # 否则投影\n",
    "            x_pass = self.proj(x)\n",
    "        \n",
    "        # 卷积路径\n",
    "        x_con1 = self.con1(x)\n",
    "        x_con2 = self.con2(x_con1)\n",
    "        \n",
    "        # 输出特征\n",
    "        x = fluid.layers.elementwise_add(x=x_pass, y=x_con1, act='relu') # 直连路径与卷积路径进行特征相加\n",
    "        \n",
    "        return x\n",
    "    \n",
    "# 模块结构\n",
    "class ResBlock(fluid.dygraph.Layer):\n",
    "    def __init__(self, in_dim, out_dim, stride=1, basics=1):\n",
    "        \"\"\"\n",
    "        功能:\n",
    "            初始化模块结构，H/W=(H/W+2*P-F)/S+1\n",
    "        输入:\n",
    "            in_dim  - 输入维度\n",
    "            out_dim - 输出维度\n",
    "            stride  - 滑动步长\n",
    "            basics  - 基础长度\n",
    "        输出:\n",
    "        \"\"\"\n",
    "        super(ResBlock, self).__init__()\n",
    "        \n",
    "        # 添加模块列表\n",
    "        self.block_list = [] # 模块列表\n",
    "        for i in range(basics):\n",
    "            block_item = self.add_sublayer( # 构造模块项目\n",
    "                'block_' + str(i),\n",
    "                ResBasic(\n",
    "                    in_dim=(in_dim if i==0 else out_dim), # 每组模块项目除第一块外，输入维度=输出维度\n",
    "                    out_dim=out_dim,\n",
    "                    stride=(stride if i==0 else 1), # 每组模块项目除第一块外，stride=1\n",
    "                    is_pass=(False if i==0 else True))) # 每组模块项目除第一块外，is_pass=True\n",
    "            self.block_list.append(block_item) # 添加模块项目\n",
    "    \n",
    "    def forward(self, x):\n",
    "        \"\"\"\n",
    "        功能:\n",
    "            对输入的特征图像提取特征\n",
    "        输入:\n",
    "            x      - 输入特征\n",
    "        输出:\n",
    "            x      - 输出特征\n",
    "        \"\"\"\n",
    "        for block_item in self.block_list:\n",
    "            x = block_item(x) # 提取模块特征\n",
    "            \n",
    "        return x\n",
    "\n",
    "# 模组结构\n",
    "class ResGroup(fluid.dygraph.Layer):\n",
    "    def __init__(self):\n",
    "        \"\"\"\n",
    "        功能:\n",
    "            初始化模组结构，H/W=(H/W+2*P-F)/S+1\n",
    "        输入:\n",
    "        输出:\n",
    "        \"\"\"\n",
    "        super(ResGroup, self).__init__()\n",
    "        \n",
    "        # 添加模组列表\n",
    "        self.group_list = [] # 模组列表\n",
    "        for i, block_arch in enumerate(group_arch):\n",
    "            group_item = self.add_sublayer( # 构造模组项目\n",
    "                'group_' + str(i),\n",
    "                ResBlock(\n",
    "                    in_dim=block_arch[0],\n",
    "                    out_dim=block_arch[1],\n",
    "                    stride=block_arch[2],\n",
    "                    basics=block_arch[3]))\n",
    "            self.group_list.append(group_item) # 添加模组项目\n",
    "    \n",
    "    def forward(self, x):\n",
    "        \"\"\"\n",
    "        功能:\n",
    "            对输入的特征图像提取特征\n",
    "        输入:\n",
    "            x      - 输入特征\n",
    "        输出:\n",
    "            x      - 输出特征\n",
    "        \"\"\"\n",
    "        for group_item in self.group_list:\n",
    "            x = group_item(x) # 提取模组特征\n",
    "            \n",
    "        return x\n",
    "        \n",
    "# 残差网络\n",
    "class ResNet(fluid.dygraph.Layer):\n",
    "    def __init__(self):\n",
    "        \"\"\"\n",
    "        功能:\n",
    "            初始化残差网络，H/W=(H/W+2*P-F)/S+1\n",
    "        输入:\n",
    "        输出:\n",
    "        \"\"\"\n",
    "        super(ResNet, self).__init__()\n",
    "        \n",
    "        # 添加初始化层\n",
    "        self.conv = ConvUnit(in_dim=3, out_dim=16, filter_size=3, stride=1, act='relu')\n",
    "        \n",
    "        # 添加模组结构\n",
    "        self.backbone = ResGroup() # 输出：N*C*H*W\n",
    "        \n",
    "        # 添加全连接层\n",
    "        self.pool = Pool2D(global_pooling=True, pool_type='avg') # 输出：N*C*1*1\n",
    "        \n",
    "        stdv = 1.0/(math.sqrt(group_dim)*1.0)                    # 设置均匀分布权重方差\n",
    "        self.fc = Linear(                                        # 输出：=N*10\n",
    "            input_dim=group_dim,\n",
    "            output_dim=class_dim,\n",
    "            param_attr=fluid.initializer.Uniform(-stdv, stdv),   # 使用均匀分布初始权重\n",
    "            bias_attr=fluid.initializer.Constant(0.0),           # 使用常量数值初始偏置\n",
    "            act='softmax')\n",
    "    \n",
    "    def forward(self, x):\n",
    "        \"\"\"\n",
    "        功能:\n",
    "            对输入图像进行分类\n",
    "        输入:\n",
    "            x - 输入图像\n",
    "        输出:\n",
    "            x - 预测结果\n",
    "        \"\"\"\n",
    "        # 提取特征\n",
    "        x = self.conv(x)\n",
    "        x = self.backbone(x)\n",
    "        \n",
    "        # 进行预测\n",
    "        x = self.pool(x)\n",
    "        x = fluid.layers.reshape(x, [x.shape[0], -1])\n",
    "        x = self.fc(x)\n",
    "        \n",
    "        return x"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tatol param: 1844180\n",
      "infer shape: [1, 100]\n"
     ]
    }
   ],
   "source": [
    "import paddle.fluid as fluid\n",
    "from paddle.fluid.dygraph.base import to_variable\n",
    "import numpy as np\n",
    "\n",
    "with fluid.dygraph.guard():\n",
    "    # 输入数据\n",
    "    x = np.random.randn(1, 3, 32, 32).astype(np.float32)\n",
    "    x = to_variable(x)\n",
    "    \n",
    "    # 进行预测\n",
    "    backbone = ResNet() # 设置网络\n",
    "    \n",
    "    infer = backbone(x) # 进行预测\n",
    "    \n",
    "    # 显示结果\n",
    "    parameters = 0\n",
    "    for p in backbone.parameters():\n",
    "        parameters += np.prod(p.shape) # 统计参数\n",
    "    \n",
    "    print('tatol param:', parameters)\n",
    "    print('infer shape:', infer.shape)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 训练模型"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAXUAAAD8CAYAAACINTRsAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDIuMi4zLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvIxREBQAAIABJREFUeJzt3Xd4VFX6wPHvmfTeSGgBQpFOQAgIUgQUREFZy09FbNh27WV1xY6Kiu6ubdeGiq4N1oorqNgoikrvHUKAhJIGSUifmfv749zJzKQHksxMeD/Pkyczd+7ce+5M8t5z31OuMgwDIYQQLYPF0wUQQgjReCSoCyFECyJBXQghWhAJ6kII0YJIUBdCiBZEgroQQrQgEtSFEKIFkaAuhBAtiAR1IYRoQfybYqOtWrUykpKSmmLTQgjRIq1ZsybbMIz4k91OkwT1pKQkVq9e3RSbFkKIFkkpta8xtiPpFyGEaEEkqAshRAsiQV0IIVqQJsmpV6e8vJz09HRKSkqaa5ctVnBwMImJiQQEBHi6KEIIL9NsQT09PZ2IiAiSkpJQSjXXblscwzDIyckhPT2dzp07e7o4Qggv02zpl5KSEuLi4iSgnySlFHFxcXLFI4SoVrPm1CWgNw75HIUQNfGqhlKrzc6xojJPF0MIIXyWVwX1fTlF7M8totxmb9Tt5uTkMGDAAAYMGECbNm1o3759xfOysvqdRKZNm8aOHTvqvc+3336bu++++0SLLIQQJ6TZGkrro9Rqa5LtxsXFsX79egBmzJhBeHg49913n9s6hmFgGAYWS/XnuXfffbdJyiaEEI3Jq2rqdqN597d792569+7N1KlT6dOnD4cOHeLmm28mJSWFPn368OSTT1asO2LECNavX4/VaiU6Oprp06fTv39/hg0bRmZmZq372bt3L2PGjCE5OZlx48aRnp4OwLx58+jbty/9+/dnzJgxAGzatInBgwczYMAAkpOTSU1NbboPQAjR4tSrpq6USgMKABtgNQwj5WR2+sTXW9h6ML/K8sJSKwChQf40tCmwd7tIHr+gT4PLsn37dt5//31SUvQhzZo1i9jYWKxWK2PGjOHSSy+ld+/ebu/Jy8vjrLPOYtasWdx7773MmTOH6dOn17iPW2+9lRtvvJGpU6cye/Zs7r77bj777DOeeOIJlixZQuvWrTl27BgAr732Gvfddx+XX345paWlGEYzn+mEED6tITX1MYZhDDjZgF4vzRjHunbtWhHQAebOncvAgQMZOHAg27ZtY+vWrVXeExISwnnnnQfAoEGDSEtLq3UfK1as4IorrgDgmmuu4ZdffgFg+PDhXHPNNbz99tvY7bod4cwzz2TmzJk8//zzHDhwgODg4MY4TCHEKcIjOfWaatQb03VttWebCAL9/ZqlLGFhYRWPd+3axcsvv8zKlSuJjo7mqquuqrY/eGBgYMVjPz8/rFbrCe37rbfeYsWKFSxYsICBAweybt06rr76aoYNG8bChQuZMGECc+bMYdSoUSe0fSHEqae+NXUD+F4ptUYpdXN1KyilblZKrVZKrc7Kymq8Ejaj/Px8IiIiiIyM5NChQyxatKhRtjt06FA++eQTAD788MOKIJ2amsrQoUN56qmniImJISMjg9TUVLp168Zdd93FpEmT2LhxY6OUQQhxaqhvTX2EYRgZSqkE4Ael1HbDMJa5rmAYxmxgNkBKSspJJVA8lUYeOHAgvXv3pmfPnnTq1Inhw4c3ynZfffVVrr/+ep599llat25d0ZPmnnvuYe/evRiGwfjx4+nbty8zZ85k7ty5BAQE0K5dO2bMmNEoZRBCnBpUQxvilFIzgOOGYfyjpnVSUlKMyjfJ2LZtG7169ap12470S/fWEQQHNE/6xVfV5/MUQvgOpdSaxmizrDP9opQKU0pFOB4D44HNJ7tjIYQQja8+6ZfWwJfmfCP+wMeGYXzXlIWSTnxCCHFi6gzqhmGkAv2boSwuO23WvQkhRIvhVSNKnSSqCyHEifCqoB4fEQRISBdCiBPlVUE9PEhng2RkvBBCnBivCupNacyYMVUGE7300kvccssttb4vPDwcgIMHD3LppZdWu87o0aOp3IWztuVCCNFUvCqoN+X9fKZMmcK8efPcls2bN48pU6bU6/3t2rXjs88+a4qiCSFEo/GqoO4I6wdyixp9y5deeikLFy6suClGWloaBw8eZOTIkRw/fpyzzz6bgQMH0q9fP7766qsq709LS6Nv374AFBcXc8UVV9CrVy8uuugiiouL69z/3Llz6devH3379uWBBx4AwGazcd1119G3b1/69evHiy++CMArr7xC7969SU5OrpgITAgh6sMzN8n4djoc3lRlcYhh0KXMvFFGUAOL1qYfnDerxpdj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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "complete - train time: 13666s, best epoch: 288, best loss: 1.223403, best accuracy: 70.32%\r"
     ]
    },
    {
     "data": {
      "text/plain": [
       "<Figure size 432x288 with 0 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "import paddle\n",
    "import paddle.fluid as fluid\n",
    "from paddle.utils.plot import Ploter\n",
    "import numpy as np\n",
    "import time\n",
    "import math\n",
    "import os\n",
    "\n",
    "epoch_num = 300   # 训练周期，取值一般为[1,300]\n",
    "train_batch = 128 # 训练批次，取值一般为[1,256]\n",
    "valid_batch = 128 # 验证批次，取值一般为[1,256]\n",
    "displays = 100    # 显示迭代\n",
    "\n",
    "start_lr = 0.00001                         # 开始学习率，取值一般为[1e-8,5e-1]\n",
    "based_lr = 0.1                             # 基础学习率，取值一般为[1e-8,5e-1]\n",
    "epoch_iters = math.ceil(50000/train_batch) # 每轮迭代数\n",
    "warmup_iter = 10 * epoch_iters             # 预热迭代数，取值一般为[1,10]\n",
    "\n",
    "momentum = 0.9     # 优化器动量\n",
    "l2_decay = 0.00005 # 正则化系数，取值一般为[1e-5,5e-4]\n",
    "epsilon = 0.05     # 标签平滑率，取值一般为[1e-2,1e-1]\n",
    "\n",
    "checkpoint = False                   # 断点标识\n",
    "model_path = './work/out/resnet'     # 模型路径\n",
    "result_txt = './work/out/result.txt' # 结果文件\n",
    "class_num  = 100                     # 类别数量\n",
    "\n",
    "with fluid.dygraph.guard():\n",
    "    # 准备数据\n",
    "    train_reader = paddle.batch(\n",
    "        reader=paddle.reader.shuffle(reader=paddle.dataset.cifar.train100(), buf_size=50000),\n",
    "        batch_size=train_batch)\n",
    "    \n",
    "    valid_reader = paddle.batch(\n",
    "        reader=paddle.dataset.cifar.test100(),\n",
    "        batch_size=valid_batch)\n",
    "    \n",
    "    # 声明模型\n",
    "    model = ResNet()\n",
    "    \n",
    "    # 优化算法\n",
    "    consine_lr = fluid.layers.cosine_decay(based_lr, epoch_iters, epoch_num) # 余弦衰减策略\n",
    "    decayed_lr = fluid.layers.linear_lr_warmup(consine_lr, warmup_iter, start_lr, based_lr) # 线性预热策略\n",
    "    \n",
    "    optimizer = fluid.optimizer.Momentum(\n",
    "        learning_rate=decayed_lr,                           # 衰减学习策略\n",
    "        momentum=momentum,                                  # 优化动量系数\n",
    "        regularization=fluid.regularizer.L2Decay(l2_decay), # 正则衰减系数\n",
    "        parameter_list=model.parameters())\n",
    "    \n",
    "    # 加载断点\n",
    "    if checkpoint: # 是否加载断点文件\n",
    "        model_dict, optimizer_dict = fluid.load_dygraph(model_path) # 加载断点参数\n",
    "        model.set_dict(model_dict)                                  # 设置权重参数\n",
    "        optimizer.set_dict(optimizer_dict)                          # 设置优化参数\n",
    "    else:          # 否则删除结果文件\n",
    "        if os.path.exists(result_txt): # 如果存在结果文件\n",
    "            os.remove(result_txt)      # 那么删除结果文件\n",
    "    \n",
    "    # 初始训练\n",
    "    avg_train_loss = 0 # 平均训练损失\n",
    "    avg_valid_loss = 0 # 平均验证损失\n",
    "    avg_valid_accu = 0 # 平均验证精度\n",
    "    \n",
    "    iterator = 1                                # 迭代次数\n",
    "    train_prompt = \"Train loss\"                 # 训练标签\n",
    "    valid_prompt = \"Valid loss\"                 # 验证标签\n",
    "    ploter = Ploter(train_prompt, valid_prompt) # 训练图像\n",
    "    \n",
    "    best_epoch = 0           # 最好周期\n",
    "    best_accu = 0            # 最好精度\n",
    "    best_loss = 100.0        # 最好损失\n",
    "    train_time = time.time() # 训练时间\n",
    "    \n",
    "    # 开始训练\n",
    "    for epoch_id in range(epoch_num):\n",
    "        # 训练模型\n",
    "        model.train() # 设置训练\n",
    "        for batch_id, train_data in enumerate(train_reader()):\n",
    "            # 读取数据\n",
    "            image_data = np.array([x[0] for x in train_data]).reshape((-1, 3, 32, 32)).astype(np.float32) # 读取图像数据\n",
    "            image_data = train_augment(image_data)                                                        # 使用数据增强\n",
    "            image = fluid.dygraph.to_variable(image_data)                                                 # 转换数据类型\n",
    "\n",
    "            label_data = np.array([x[1] for x in train_data]).astype(np.int64)                        # 读取标签数据\n",
    "            label = fluid.dygraph.to_variable(label_data)                                             # 转换数据类型\n",
    "            label = fluid.layers.label_smooth(label=fluid.one_hot(label, class_num), epsilon=epsilon) # 使用标签平滑\n",
    "            label.stop_gradient = True                                                                # 停止梯度传播\n",
    "\n",
    "            # 前向传播\n",
    "            infer = model(image)\n",
    "            \n",
    "            # 计算损失\n",
    "            loss = fluid.layers.cross_entropy(infer, label, soft_label=True)\n",
    "            train_loss = fluid.layers.mean(loss)\n",
    "            \n",
    "            # 反向传播\n",
    "            train_loss.backward()\n",
    "            optimizer.minimize(train_loss)\n",
    "            model.clear_gradients()\n",
    "            \n",
    "            # 显示结果\n",
    "            if iterator % displays == 0:\n",
    "                # 显示图像\n",
    "                avg_train_loss = train_loss.numpy()[0]                # 设置训练损失\n",
    "                ploter.append(train_prompt, iterator, avg_train_loss) # 添加训练图像\n",
    "                ploter.plot()                                         # 显示训练图像\n",
    "                \n",
    "                # 打印结果\n",
    "                print(\"iteration: {:6d}, epoch: {:3d}, train loss: {:.6f}, valid loss: {:.6f}, valid accuracy: {:.2%}\".format(\n",
    "                    iterator, epoch_id+1, avg_train_loss, avg_valid_loss, avg_valid_accu))\n",
    "                \n",
    "                # 写入文件\n",
    "                with open(result_txt, 'a') as file:\n",
    "                    file.write(\"iteration: {:6d}, epoch: {:3d}, train loss: {:.6f}, valid loss: {:.6f}, valid accuracy: {:.2%}\\n\".format(\n",
    "                        iterator, epoch_id+1, avg_train_loss, avg_valid_loss, avg_valid_accu))\n",
    "            \n",
    "            # 增加迭代\n",
    "            iterator += 1\n",
    "            \n",
    "        # 验证模型\n",
    "        valid_loss_list = [] # 验证损失列表\n",
    "        valid_accu_list = [] # 验证精度列表\n",
    "        \n",
    "        model.eval()   # 设置验证\n",
    "        for batch_id, valid_data in enumerate(valid_reader()):\n",
    "            # 读取数据\n",
    "            image_data = np.array([x[0] for x in valid_data]).reshape((-1, 3, 32, 32)).astype(np.float32) # 读取图像数据\n",
    "            image_data = valid_augment(image_data)                                                        # 使用图像增强\n",
    "            image = fluid.dygraph.to_variable(image_data)                                                 # 转换数据类型\n",
    "            \n",
    "            label_data = np.array([x[1] for x in valid_data]).reshape((-1, 1)).astype(np.int64) # 读取标签数据\n",
    "            label = fluid.dygraph.to_variable(label_data)                                       # 转换数据类型\n",
    "            label.stop_gradient = True                                                          # 停止梯度传播\n",
    "            \n",
    "            # 前向传播\n",
    "            infer = model(image)\n",
    "            \n",
    "            # 计算精度\n",
    "            valid_accu = fluid.layers.accuracy(infer,label)\n",
    "            \n",
    "            valid_accu_list.append(valid_accu.numpy())\n",
    "            \n",
    "            # 计算损失\n",
    "            loss = fluid.layers.cross_entropy(infer, label)\n",
    "            valid_loss = fluid.layers.mean(loss)\n",
    "            \n",
    "            valid_loss_list.append(valid_loss.numpy())\n",
    "        \n",
    "        # 设置结果\n",
    "        avg_valid_accu = np.mean(valid_accu_list)             # 设置验证精度\n",
    "        \n",
    "        avg_valid_loss = np.mean(valid_loss_list)             # 设置验证损失\n",
    "        ploter.append(valid_prompt, iterator, avg_valid_loss) # 添加训练图像\n",
    "        \n",
    "        # 保存模型\n",
    "        fluid.save_dygraph(model.state_dict(), model_path)     # 保存权重参数\n",
    "        fluid.save_dygraph(optimizer.state_dict(), model_path) # 保存优化参数\n",
    "        \n",
    "        if avg_valid_loss < best_loss:\n",
    "            fluid.save_dygraph(model.state_dict(), model_path + '-best') # 保存权重\n",
    "            \n",
    "            best_epoch = epoch_id + 1                                    # 更新迭代\n",
    "            best_accu = avg_valid_accu                                   # 更新精度\n",
    "            best_loss = avg_valid_loss                                   # 更新损失\n",
    "    \n",
    "    # 显示结果\n",
    "    train_time = time.time() - train_time # 设置训练时间\n",
    "    print('complete - train time: {:.0f}s, best epoch: {:3d}, best loss: {:.6f}, best accuracy: {:.2%}'.format(\n",
    "        train_time, best_epoch, best_loss, best_accu))\n",
    "    \n",
    "    # 写入文件\n",
    "    with open(result_txt, 'a') as file:\n",
    "        file.write('complete - train time: {:.0f}s, best epoch: {:3d}, best loss: {:.6f}, best accuracy: {:.2%}\\n'.format(\n",
    "            train_time, best_epoch, best_loss, best_accu))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 模型预测"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "infer time: 0.039484s, infer value: cattle\n"
     ]
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 216x216 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "import paddle.fluid as fluid\n",
    "from PIL import Image\n",
    "import numpy as np\n",
    "import time\n",
    "import matplotlib.pyplot as plt\n",
    "\n",
    "image_path = './work/out/img.png' # 图片路径\n",
    "model_path = './work/out/resnet-best' # 模型路径\n",
    "\n",
    "# 加载图像\n",
    "def load_image(image_path):\n",
    "    \"\"\"\n",
    "    功能:\n",
    "        读取图像并转换到输入格式\n",
    "    输入:\n",
    "        image_path - 输入图像路径\n",
    "    输出:\n",
    "        image - 输出图像\n",
    "    \"\"\"\n",
    "    # 读取图像\n",
    "    image = Image.open(image_path) # 打开图像文件\n",
    "    \n",
    "    # 转换格式\n",
    "    image = image.resize((32, 32), Image.ANTIALIAS) # 调整图像大小\n",
    "    image = np.array(image, dtype=np.float32) # 转换数据格式，数据类型转换为float32\n",
    "\n",
    "    # 减去均值\n",
    "    mean = np.array([0.4914, 0.4822, 0.4465]).reshape((1, 1, -1)) # cifar数据集通道平均值\n",
    "    stdv = np.array([0.2471, 0.2435, 0.2616]).reshape((1, 1, -1)) # cifar数据集通道标准差\n",
    "    \n",
    "    image = (image/255.0 - mean) / stdv # 对图像进行归一化\n",
    "    image = image.transpose((2, 0, 1)).astype(np.float32) # 数据格式从HWC转换为CHW，数据类型转换为float32\n",
    "    \n",
    "    # 增加维度\n",
    "    image = np.expand_dims(image, axis=0) # 增加数据维度\n",
    "    \n",
    "    return image\n",
    "\n",
    "# 预测图像\n",
    "with fluid.dygraph.guard():\n",
    "    # 读取图像\n",
    "    image = load_image(image_path)\n",
    "    image = fluid.dygraph.to_variable(image)\n",
    "    \n",
    "    # 加载模型\n",
    "    model = ResNet()                               # 加载模型\n",
    "    model_dict, _ = fluid.load_dygraph(model_path) # 加载权重\n",
    "    model.set_dict(model_dict)                     # 设置权重\n",
    "    model.eval()                                   # 设置验证\n",
    "    \n",
    "    # 前向传播\n",
    "    infer_time = time.time()              # 推断开始时间\n",
    "    infer = model(image)\n",
    "    infer_time = time.time() - infer_time # 推断结束时间\n",
    "    \n",
    "    # 显示结果\n",
    "    vlist = ['beaver', 'dolphin', 'otter', 'seal', 'whale',\n",
    "             'aquarium fish', 'flatfish', 'ray', 'shark', 'trout',\n",
    "             'orchids', 'poppies', 'roses', 'sunflowers', 'tulips',\n",
    "             'bottles', 'bowls', 'cans', 'cups', 'plates',\n",
    "             'apples', 'mushrooms', 'oranges', 'pears', 'sweet peppers',\n",
    "             'clock', 'keyboard', 'lamp', 'telephone', 'television',\n",
    "             'bed', 'chair', 'couch', 'table', 'wardrobe',\n",
    "             'bee', 'beetle', 'butterfly', 'caterpillar', 'cockroach',\n",
    "             'bear', 'leopard', 'lion', 'tiger', 'wolf',\n",
    "             'bridge', 'castle', 'house', 'road', 'skyscraper',\n",
    "             'cloud', 'forest', 'mountain', 'plain', 'sea',\n",
    "             'camel', 'cattle', 'chimpanzee', 'elephant', 'kangaroo',\n",
    "             'fox', 'porcupine', 'possum', 'raccoon', 'skunk',\n",
    "             'crab', 'lobster', 'snail', 'spider', 'worm',\n",
    "             'baby', 'boy', 'girl', 'man', 'woman',\n",
    "             'crocodile', 'dinosaur', 'lizard', 'snake', 'turtle',\n",
    "             'hamster', 'mouse', 'rabbit', 'shrew', 'squirrel',\n",
    "             'maple', 'oak', 'palm', 'pine', 'willow',\n",
    "             'bicycle', 'bus', 'motorcycle', 'pickup truck', 'train',\n",
    "             'lawn-mower', 'rocket', 'streetcar', 'tank', 'tractor'] # 标签名称列表\n",
    "    vlist.sort() # 字母上升排序\n",
    "    print('infer time: {:f}s, infer value: {}'.format(infer_time, vlist[np.argmax(infer.numpy())]) )\n",
    "    \n",
    "    image = Image.open(image_path) # 打开图像文件\n",
    "    plt.figure(figsize=(3, 3))     # 设置显示大小\n",
    "    plt.imshow(image)              # 设置显示图像\n",
    "    plt.show()                     # 显示图像文件"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
